#include "StdAfx.h"
#include "MyHttpfile.h"
#include <cassert>
#include <sstream>
#include <fstream>
#include <direct.h>

#ifdef _WINDOWS
WSAData wsaData;
#endif
using namespace std;
static bool strsubstr(const string &source,
					string &dst,
					const string &tofind,
					const string &tofindtail,
					string &save)
{
	string::size_type pos,postail;
	const string *_source;
	if(!save.empty())
	{
		_source=&save;			
	}
	else
	{
		_source=&source;
	}

	pos=_source->find(tofind);	
	if(pos!=string::npos)
	{
		if(!tofindtail.empty())
		{
			postail=_source->find(tofindtail,pos+tofind.length());
			if(postail!=string::npos)
			{
				dst=_source->substr(pos+tofind.length(),postail-pos-tofind.length());
				save=_source->substr(postail);
			}
			else
			{
				dst.clear();
				save.clear();
				return false;
			}
		}
		else
		{
			dst=_source->substr(pos+tofind.length());
			save.clear();
		}
		return true;
	}
	else
	{
		dst.clear();
		save.clear();
		return false;
	}
	return false;
}

template<typename F,typename T>
static T mycast(F val)
{
	stringstream ss;
	T ret;
	ss<<val;
	ss>>ret;
	return ret;
}



static void _WSAinit()
{
#ifdef _WINDOWS
	static bool inited;
	if(!inited)
	{
		ASSERT(0==WSAStartup(MAKEWORD(2,2),&wsaData));
		inited=true;
	}
#endif
}

MyHttpfile::MyHttpfile(void)
{
	_WSAinit();
	m_socket=::socket(AF_INET,SOCK_STREAM,0);
	assert(m_socket!=INVALID_SOCKET);
	
}

static string strtolower(const string &str)
{
	const char *p=str.c_str();
	size_t l=str.length();
	char *s=new char[l];
	for(size_t i=0;i<l;i++)
		s[i]=tolower(p[i]);
	string ret(s,l);
	delete []s;
	return ret;
}

static string strtoupper(const string &str)
{
	const char *p=str.c_str();
	size_t l=str.length();
	char *s=new char[l];
	for(size_t i=0;i<l;i++)
		s[i]=toupper(p[i]);
	string ret(s,l);
	delete []s;
	return ret;
}

MyHttpfile::~MyHttpfile(void)
{
	closesocket(m_socket);
}

bool MyHttpfile::open(const std::string &url)
{	
	return parseurl(url);
}

bool MyHttpfile::parseurl(const string &url)
{
	string savestr,tmp;
	if(strsubstr(url,tmp,"","://",savestr))
	{
		strsubstr("",tmp,"://","/",savestr);
	}
	else
	{
		strsubstr(url,tmp,"","/",savestr);
	}

	if(tmp.empty())
	{
		return false;
	}

	m_sendheader.host=tmp;

	if(savestr.empty())
	{
		m_sendheader.uri=DEFAULT_URI;
	}
	else
	{
		m_sendheader.uri=savestr;
	}

	savestr.clear();
	if(strsubstr(m_sendheader.host,tmp,"",":",savestr))
	{		
		m_host=tmp;
		m_port=savestr.substr(1);
		if(m_port.empty())
		{
			m_port=DEFAULT_PORT;
		}
	}
	else
	{
		m_host=m_sendheader.host;
		m_port=DEFAULT_PORT;
	}
	
	return true;
}

bool MyHttpfile::connect()
{
	sockaddr_in addr;

	memset(&addr,0,sizeof(addr));
	addr.sin_family=AF_INET;
	addr.sin_port=htons(mycast<string,u_short>(m_port));
	hostent *ht;
	ht=gethostbyname(m_host.c_str());
	if(!ht)
	{
		return false;
	}
	addr.sin_addr.s_addr=*(u_long*)ht->h_addr;
	if(SOCKET_ERROR==::connect(m_socket,(const sockaddr*)&addr,sizeof(addr)))
	{
		return false;
	}
	return true;
}

bool MyHttpfile::sendheader()
{
	m_sendheader.str.clear();
	m_sendheader.str=HTTP_GET;
	m_sendheader.str.append(m_sendheader.uri);
	m_sendheader.str.append(HTTP_SPACE);
	m_sendheader.str.append(HTTP_VER);
	m_sendheader.str.append(HTTP_ENDLINE);

	m_sendheader.str.append(HTTP_HOST);
	m_sendheader.str.append(m_sendheader.host);
	m_sendheader.str.append(HTTP_ENDLINE);

	m_sendheader.str.append(HTTP_ACCEPT);
	m_sendheader.str.append("*/*");
	m_sendheader.str.append(HTTP_ENDLINE);

	m_sendheader.str.append(HTTP_ENDLINE);

	string tosend=m_sendheader.str;
	for(string::size_type last=tosend.length();last>0;)
	{
		int sended=::send(m_socket,tosend.data(),(int)tosend.length(),0);
		if(sended<1)
		{
			return false;
		}
		last-=(string::size_type)sended;
		tosend=tosend.substr(sended);
	}

	return false;
}

bool MyHttpfile::receive()
{
	char buf[MAX_RECVBUF];
	int recved;
	

	m_rescontent.clear();

	if(recline().length())
	{
		string save,res;
		if(!strsubstr(m_recvtmp,res,""," ",save))
		{
			return false;
		}
		m_responseheader.ver=res;

		if(!strsubstr("",res," "," ",save))
		{
			return false;
		}

		m_responseheader.rescode=res;

		if(!strsubstr("",res," ","",save))
		{
			return false;
		}

		m_responseheader.tip=res;
	}

	while(recline().length())
	{
		string save,res;
		if(!strsubstr(m_recvtmp,res,""," ",save))
		{
			return false;
		}
		m_responseheader.property.insert(pair<const string,string>(strtoupper(res.substr(0,res.length()-1)),save.substr(1)));
	}
	dword content_length=0;
	MKV::iterator itr;
	if((itr=m_responseheader.property.find("CONTENT-LENGTH"))!=m_responseheader.property.end())
	{
		if((content_length=mycast<string,dword>(itr->second))>1024*1024*5)
		{
			char tmpname[256]="tmp";
			mkdir("tmp");
			
			m_recvfstream.open(tmpnam(tmpname+3)-3,ios_base::out|ios_base::binary|ios_base::trunc);
			if(m_recvfstream.is_open())
			{
				while((recved=::recv(m_socket,buf,MAX_RECVBUF,0))>0)
				{
					m_recvfstream.write(buf,recved);
				}
				m_recvfstream.close();
				m_recvfstream.open(tmpname,ios_base::in|ios_base::binary);
				m_resstream=&m_recvfstream;
				return true;
			}
			else
			{
				return false;
			}
		}
	}
	while((recved=::recv(m_socket,buf,MAX_RECVBUF,0))>0)
	{
		m_recvsstream.write(buf,recved);
		
	}
	m_resstream=&m_recvsstream;
	return true;
}

bool MyHttpfile::perform()
{
	if(!connect())
	{
		return false;
	}
	sendheader();
	receive();
	return true;
}

const string &MyHttpfile::rescontent()
{	
	string tmp;
	m_rescontent.clear();
	m_resstream->seekg(0);
	while(*m_resstream>>tmp)
	{	
		m_rescontent.append(tmp);
	}
	return m_rescontent;
}
istream &MyHttpfile::resstream()
{
	m_resstream->seekg(0);
	return &m_resstream;
}
string &MyHttpfile::recline()
{
	size_t reced,i=0;
	m_recvtmp.resize(MAX_RECVBUF);
	char *buf=(char*)m_recvtmp.data();
	while((reced=recv(m_socket,buf+(i++),1,0))==1)
	{
		if(i==MAX_RECVBUF)
		{
			m_recvtmp.clear();
			break;
		}

		if((buf[i-1]=='\n')&&(i>1)&&(buf[i-2]=='\r'))
		{
			m_recvtmp.resize(i-2);
			break;
		}
	}
	return m_recvtmp;
}